//
// Created by v on 19-6-25.
//

#include <unistd.h>
#include <vector>

#include "CMDatabaseSqlite.h"
#include "sqlite3.h"

CMDatabaseSqlite::CMDatabaseSqlite(const char *dbfile) : CMDatabaseBase(dbfile)
{
}

CMDatabaseSqlite::~CMDatabaseSqlite() { dbDisconnect(); }

int CMDatabaseSqlite::dbConnect()
{
    if (m_handler && m_valid)
        return 0;
    if (access(m_dbfile.c_str(), F_OK) != 0)
        return -1;

    m_handler = NULL;
    int ret   = sqlite3_open_v2(m_dbfile.c_str(), &m_handler,
                              SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
                                  SQLITE_OPEN_FULLMUTEX,
                              NULL);
    if (ret != SQLITE_OK)
    {
        return -2;
    }
    m_valid = true;
    return 0;
}

int CMDatabaseSqlite::dbDisconnect()
{
    if (m_handler)
    {
        sqlite3_close_v2(m_handler);
    }
    m_handler = NULL;
    m_valid   = false;
    return 0;
}

int CMDatabaseSqlite::dbQuery(const char *sql, CMDatabaseResult &result)
{
    if (!m_handler || !m_valid)
        return -1;

    char *errmsg = NULL;
    dbGetLock(200);
    int rc = sqlite3_exec(m_handler, sql, NULL, NULL, &errmsg);
    if (rc != SQLITE_OK)
    {
        if (errmsg)
        {
            result.m_errMsg = errmsg;
            sqlite3_free(errmsg);
        }
        return -2;
    }
    return 0;
}

int CMDatabaseSqlite::dbQueryResult(const char *sql, CMDatabaseResult &result)
{
    if (!m_handler || !m_valid)
        return -1;

    char *errmsg = NULL;
    dbGetLock(200);
    char **dbresult = NULL;
    int    nrow     = 0;
    int    ncolum   = 0;
    int    rc =
        sqlite3_get_table(m_handler, sql, &dbresult, &nrow, &ncolum, &errmsg);
    if (rc != SQLITE_OK)
    {
        if (errmsg)
        {
            result.m_errMsg = errmsg;
            sqlite3_free(errmsg);
        }
        sqlite3_free_table(dbresult);
        return -2;
    }
    else
    {
        int                      i = 0;
        std::vector<std::string> tmphead;
        for (i = 0; i < ncolum; i++)
        {
            result.m_head.push_back(dbresult[i]);
            tmphead.push_back(dbresult[i]);
        }
        // nrow not include head
        for (auto i = 1; i < nrow + 1; i++)
        {
            int                                j;
            std::vector<std::string>           tmpline;
            std::map<std::string, std::string> tmplist;
            for (j = 0; j < ncolum; j++)
            {
                tmpline.push_back(dbresult[i * ncolum + j]);
                if (dbresult[i * ncolum + j])
                {
                    tmplist[tmphead[j]] = dbresult[i * ncolum + j];
                }
            }
            result.m_result.push_back(tmpline);
            result.m_resultEx.push_back(tmplist);
        }

        sqlite3_free_table(dbresult);
    }

    return 0;
}

static int sqlite_busy_cb(void *ptr, int count)
{
    int timeout = *((int *)ptr);
    usleep(timeout);
    return 1;
}

int CMDatabaseSqlite::dbGetLock(int ms)
{
    if (ms > 0)
    {
        sqlite3_busy_handler(m_handler, sqlite_busy_cb, (void *)&ms);
    }
    else
    {
        sqlite3_busy_handler(m_handler, NULL, NULL);
    }
}
